package com.jsb.laundry.common.utils.http;

import com.jsb.laundry.common.constant.CommonConstants;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

import static java.util.Objects.isNull;

/**
 * sh256、md5、base64、url
 */
public class EncryptUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(EncryptUtil.class);

    public static void main(String[] args) {

    }

    /**
     * Base64
     */
    public static class Base64Tool {
        /**
         * 将字符串进行 BASE64 编码
         *
         * @param data 需要进行编码的字符串
         * @return 编码后的字符串
         */
        public static String base64Encode(String data) {
            byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
            return Base64.getEncoder().encodeToString(bytes);
        }

        /**
         * 将 BASE64 编码的字符串进行解码
         *
         * @param base64Str 需要进行解码的字符串
         * @return 解码后的字符串
         */
        public static String base64Decode(String base64Str) {
            byte[] bytes = Base64.getDecoder().decode(base64Str);
            return new String(bytes, StandardCharsets.UTF_8);
        }

        /**
         * websocket中base64编码
         *
         * @param data
         * @return
         */
        public static String wsBase64Encode(String data) {
            String commonBase64Str = base64Encode(data);
            String wsBase64Str = commonBase64Str.replaceAll("=", "+");
            return wsBase64Str;
        }

        /**
         * websocket中base64解码
         *
         * @param wsBase64Str
         * @return
         */
        public static String wsBase64Decode(String wsBase64Str) {
            String commonStr = wsBase64Str.replaceAll("\\+", "=");
            String data = base64Decode(commonStr);
            return data;
        }

    }

    /**
     * URL编码解码
     */
    public static class URLTool {

        /**
         * url内容编码
         *
         * @param str 内容
         * @return 编码后的内容
         */
        public static String urlEncode(String str) {
            try {
                return URLEncoder.encode(str, CommonConstants.UTF8);
            } catch (UnsupportedEncodingException e) {
                return CommonConstants.EMPTY_STR;
            }
        }

        /**
         * url内容解码
         *
         * @param str 内容
         * @return 解码后的内容
         */
        public static String urlDecode(String str) {
            try {
                return URLDecoder.decode(str, CommonConstants.UTF8);
            } catch (UnsupportedEncodingException e) {
                return CommonConstants.EMPTY_STR;
            }
        }

    }

    /**
     * MD5
     */
    public static class MD5Tool {

        /**
         * MD5 hash运算
         */
        private static final String ALGORITHM = "MD5";

        /**
         * 用于将数据和盐值进行哈希运算
         *
         * @param data 数据
         * @return
         */
        private static String md5Hash(String data) {
            String salt = generateSalt();
            LOGGER.info("将数据进行hash运算,随机盐为：", salt);
            return md5Hash(data, salt);
        }

        /**
         * 用于将数据和盐值进行哈希运算
         *
         * @param data 数据
         * @param salt 盐
         * @return
         */
        private static String md5Hash(String data, String salt) {
            try {
                MessageDigest md = MessageDigest.getInstance(ALGORITHM);
                md.update(hexToBytes(salt));
                md.update(data.getBytes());
                byte[] hashedPasswordBytes = md.digest();
                return bytesToHex(hashedPasswordBytes);
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
            }
        }

        /**
         * 用于生成一个随机的盐值
         */
        private static String generateSalt() {
            SecureRandom random = new SecureRandom();
            byte[] saltBytes = new byte[16];
            random.nextBytes(saltBytes);
            return bytesToHex(saltBytes);
        }

        /**
         * byte[]转字符串
         *
         * @param bytes
         * @return
         */
        private static String bytesToHex(byte[] bytes) {
            StringBuilder hexBuilder = new StringBuilder();
            for (byte b : bytes) {
                hexBuilder.append(String.format("%02x", b));
            }
            return hexBuilder.toString();
        }

        /**
         * 字符串转byte[]
         *
         * @param hex
         * @return
         */
        private static byte[] hexToBytes(String hex) {
            byte[] bytes = new byte[hex.length() / 2];
            for (int i = 0; i < bytes.length; i++) {
                bytes[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
            }
            return bytes;
        }

    }

    /**
     * SHA256 不可逆加密算法
     */
    public static class SHA256Tool {
        /**
         * SHA-256加密算法
         */
        private static final String ALGORITHM = "SHA-256";

        /**
         * SHA-256加密算法
         *
         * @param data
         * @return
         */
        public static String sh256Encrypt(String data) {
            return sh256Encrypt(data, null);
        }

        /**
         * SHA-256加密算法加盐
         *
         * @param data 数据
         * @param salt 加盐
         * @return
         */
        public static String sh256Encrypt(String data, String salt) {
            byte[] encrypt = sh256Encrypt(data.getBytes(), salt);
            return toHexString(encrypt);
        }

        /**
         * SHA-256加密算法初始封装
         *
         * @param data 数据
         * @param salt 加盐
         * @return
         */
        public static byte[] sh256Encrypt(byte[] data, String salt) {
            MessageDigest messageDigest = null;
            try {
                messageDigest = MessageDigest.getInstance(ALGORITHM);
                if (!isNull(salt) && !"".equals(salt.trim())) {
                    SecureRandom random = new SecureRandom();
                    byte[] plusSalt = salt.getBytes();
                    random.nextBytes(plusSalt);
                    messageDigest.update(plusSalt);
                }
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
            }
            return messageDigest.digest(data);
        }

        /**
         * 对SHA-256加密算法初始封装结果进行转十六进制
         *
         * @param bytes
         * @return
         */
        private static String toHexString(byte[] bytes) {
            StringBuilder hexString = new StringBuilder();
            for (byte b : bytes) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString();
        }

        /**
         * 十六进制字符串
         *
         * @param byteArray
         * @return
         */
        private static String byteToString(byte[] byteArray) {
            if (byteArray == null || byteArray.length < 1)
                throw new IllegalArgumentException("this byteArray must not be null or empty");

            final StringBuilder hexString = new StringBuilder();
            for (int i = 0; i < byteArray.length; i++) {
                if ((byteArray[i] & 0xff) < 0x10)//0~F前面不零
                    hexString.append("0");
                hexString.append(Integer.toHexString(0xFF & byteArray[i]));
            }
            return hexString.toString().toLowerCase();
        }

    }

    /**
     * AES 对称可逆加密算法
     */
    public static class AESTool {

        /**
         * AES加密算法
         */
        private static final String ALGORITHM = "AES";
        /**
         * AES加密算法/模式/补码方式
         */
        private static final String FILL_MODE = "AES/CBC/PKCS5Padding";
        /**
         * 密钥
         */
        private static final String PRIVATE_KEY = "fedcba9876543210";
        /**
         * 偏移量
         */
        private static final String OFFSET = "0123456789abcdef";

        /**
         * AES 加密
         *
         * @param data 待加密密内容
         * @return
         */
        public static String AESEncrypt(String data) {
            try {
                SecretKeySpec secretKey = new SecretKeySpec(PRIVATE_KEY.getBytes(), ALGORITHM);
                // 创建密码器
                Cipher cipher = Cipher.getInstance(FILL_MODE);
                byte[] byteContent = data.getBytes(StandardCharsets.UTF_8);
                cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(OFFSET.getBytes()));
                byte[] encrypted = cipher.doFinal(byteContent);
                return Base64.getEncoder().encodeToString(encrypted); // 此处使用BASE64做转码功能，同时能起到2次加密的作用。
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        /**
         * AES 解密
         *
         * @param data 待解密内容
         * @return
         */

        public static String AESDecrypt(String data) {
            try {
                byte[] byteContent = Base64.getDecoder().decode(data.getBytes());
                SecretKeySpec skey = new SecretKeySpec(PRIVATE_KEY.getBytes(), ALGORITHM);
                // 创建密码器
                Cipher cipher = Cipher.getInstance(FILL_MODE);
                cipher.init(Cipher.DECRYPT_MODE, skey, new IvParameterSpec(OFFSET.getBytes()));
                byte[] decrypted = cipher.doFinal(byteContent);
                return new String(decrypted, StandardCharsets.UTF_8); // 明文输出
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        /**
         * 生成随机AES密钥,密钥长度128位
         *
         * @return AES明文密钥Base64字符串
         */
        public static String genAesRandomKey2Base64Str() {
            return Base64.getEncoder().encodeToString(genAesRandomKey());
        }

        /**
         * 生成随机AES密钥,密钥长度128位
         *
         * @return AES明文密钥字节数组
         */
        public static byte[] genAesRandomKey() {
            KeyGenerator keygen = null;
            try {
                keygen = KeyGenerator.getInstance(ALGORITHM);
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException("生成随机AES密钥失败!", e);
            }
            SecureRandom random = new SecureRandom();
            keygen.init(random);
            Key key = keygen.generateKey();
            return key.getEncoded();
        }

    }

    /**
     * 微信解密
     */
    public static class WechatAESTool {
        private static boolean initialized = false;

        public static void initialize() {
            if (initialized) {
                return;
            }
            Security.addProvider(new BouncyCastleProvider());
            initialized = true;
        }

        /**
         * 解密手机号
         *
         * @param encryptedData
         * @param iv
         * @param sessionKey
         * @return
         */
        public static String AESDecrypt(String encryptedData, String iv, String sessionKey) {
            byte[] resultByte = WechatAESTool.decrypt(com.alibaba.nacos.common.codec.Base64.decodeBase64(encryptedData.getBytes(StandardCharsets.UTF_8)), com.alibaba.nacos.common.codec.Base64.decodeBase64(sessionKey.getBytes(StandardCharsets.UTF_8)), com.alibaba.nacos.common.codec.Base64.decodeBase64(iv.getBytes(StandardCharsets.UTF_8)));
            //判断返回参数是否为空
            if (null != resultByte && resultByte.length > 0) {
                String jsons = new String(resultByte, StandardCharsets.UTF_8);

                return jsons;
            }
            return null;
        }

        public static byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) {
            initialize();
            try {

                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");

                Key sKeySpec = new SecretKeySpec(keyByte, "AES");

                cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化

                byte[] result = cipher.doFinal(content);

                return result;

            } catch (Exception e) {

                e.printStackTrace();

            }
            return null;
        }

        //生成iv
        public static AlgorithmParameters generateIV(byte[] iv) throws Exception {

            AlgorithmParameters params = AlgorithmParameters.getInstance("AES");

            params.init(new IvParameterSpec(iv));

            return params;

        }


    }

}
